[PATCH 1/3] dcerpc: add upper limit on stub data
authorShivani Bhardwaj <shivani@oisf.net>
Tue, 6 Jan 2026 11:14:52 +0000 (16:44 +0530)
committerAndreas Dolp <dev@andreas-dolp.de>
Sun, 22 Feb 2026 12:28:52 +0000 (13:28 +0100)
DCERPC parsers had no upper bounds when it came to extending the stub
data buffer. Traffic can be crafted to bypass some internal parser
conditions to create an indefinite buffering in the stub_data array that
can make Suricata crash.

Add a default limit of 1MiB and make it configurable for the user.

Security 8182

Co-authored-by: Philippe Antoine <pantoine@oisf.net>
(cherry picked from commit e412215af990feeffbb66c7dd9f392813a20ae50)

Origin: upstream, https://github.com/OISF/suricata/commit/f82a388d0283725cb76782cf64e8341cab370830.patch
Bug: https://redmine.openinfosecfoundation.org/issues/8182
Subject: Upstream fix for CVE-2026-22258 part 1

Gbp-Pq: Name CVE-2026-22258_1.patch

rust/src/dcerpc/dcerpc.rs
rust/src/dcerpc/dcerpc_udp.rs
rust/src/smb/dcerpc.rs
rust/src/smb/smb.rs
suricata.yaml.in

index b99b23d3f56206bdcb13213bf9ba20108c98096c..75fc151c6cb759477fe42335140fba8dbf9e85b7 100644 (file)
@@ -25,7 +25,9 @@ use std;
 use std::cmp;
 use std::ffi::CString;
 use std::collections::VecDeque;
-use crate::conf::conf_get;
+use crate::conf::{conf_get, get_memval};
+
+pub static mut DCERPC_MAX_STUB_SIZE: u32 = 1048576;
 
 // Constant DCERPC UDP Header length
 pub const DCERPC_HDR_LEN: u16 = 16;
@@ -163,6 +165,11 @@ pub fn get_req_type_for_resp(t: u8) -> u8 {
         _ => DCERPC_TYPE_UNKNOWN,
     }
 }
+#[inline(always)]
+pub fn cfg_max_stub_size() -> u32 {
+    unsafe { DCERPC_MAX_STUB_SIZE }
+}
+
 
 #[derive(Default, Debug)]
 pub struct DCERPCTransaction {
@@ -1109,7 +1116,12 @@ fn evaluate_stub_params(
     }
 
     let input_slice = &input[..stub_len as usize];
-    stub_data_buffer.extend_from_slice(input_slice);
+    let max_size = cfg_max_stub_size() as usize;
+    if (stub_data_buffer.len() + input_slice.len()) < max_size {
+        stub_data_buffer.extend_from_slice(input_slice);
+    } else if stub_data_buffer.len() < max_size {
+        stub_data_buffer.extend_from_slice(&input_slice[..max_size - stub_data_buffer.len()]);
+    }
 
     stub_len
 }
@@ -1409,6 +1421,21 @@ pub unsafe extern "C" fn rs_dcerpc_register_parser() {
             }
         }
         SCLogDebug!("Rust DCERPC parser registered.");
+        let retval = conf_get("app-layer.protocols.dcerpc.max-stub-size");
+        if let Some(val) = retval {
+            match get_memval(val) {
+                Ok(retval) => {
+                    if retval > 0 {
+                        DCERPC_MAX_STUB_SIZE = retval as u32;
+                    } else {
+                        SCLogError!("Invalid max-stub-size value");
+                    }
+                }
+                Err(_) => {
+                    SCLogError!("Invalid max-stub-size value");
+                }
+            }
+        }
     } else {
         SCLogDebug!("Protocol detector and parser disabled for DCERPC.");
     }
index 700e38fe36b1253125cae350d1b4441e2847ebb5..d551b8674dc7611839dbfcae80cc1ea7e7c696d0 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (C) 2020 Open Information Security Foundation
+/* Copyright (C) 2020-2026 Open Information Security Foundation
  *
  * You can copy, redistribute or modify this Program under the terms of
  * the GNU General Public License version 2 as published by the Free
@@ -19,7 +19,7 @@ use crate::applayer::{self, *};
 use crate::core::{self, Direction, DIR_BOTH};
 use crate::dcerpc::dcerpc::{
     DCERPCTransaction, DCERPC_MAX_TX, DCERPC_TYPE_REQUEST, DCERPC_TYPE_RESPONSE, PFCL1_FRAG, PFCL1_LASTFRAG,
-    rs_dcerpc_get_alstate_progress, ALPROTO_DCERPC, PARSER_NAME,
+    rs_dcerpc_get_alstate_progress, ALPROTO_DCERPC, PARSER_NAME, cfg_max_stub_size,
 };
 use nom7::Err;
 use std;
@@ -171,18 +171,27 @@ impl DCERPCUDPState {
             tx.tx_data.updated_ts = true;
             let done = (hdr.flags1 & PFCL1_FRAG) == 0 || (hdr.flags1 & PFCL1_LASTFRAG) != 0;
 
+            let max_size = cfg_max_stub_size() as usize;
             match hdr.pkt_type {
                 DCERPC_TYPE_REQUEST => {
-                    tx.stub_data_buffer_ts.extend_from_slice(input);
                     tx.frag_cnt_ts += 1;
+                    if input.len() + tx.stub_data_buffer_ts.len() < max_size {
+                        tx.stub_data_buffer_ts.extend_from_slice(input);
+                    } else if tx.stub_data_buffer_ts.len() < max_size {
+                        tx.stub_data_buffer_ts.extend_from_slice(&input[..max_size - tx.stub_data_buffer_ts.len()]);
+                    }
                     if done {
                         tx.req_done = true;
                     }
                     return true;
                 }
                 DCERPC_TYPE_RESPONSE => {
-                    tx.stub_data_buffer_tc.extend_from_slice(input);
                     tx.frag_cnt_tc += 1;
+                    if input.len() + tx.stub_data_buffer_tc.len() < max_size {
+                        tx.stub_data_buffer_tc.extend_from_slice(input);
+                    } else if tx.stub_data_buffer_tc.len() < max_size {
+                        tx.stub_data_buffer_tc.extend_from_slice(&input[..max_size - tx.stub_data_buffer_tc.len()]);
+                    }
                     if done {
                         tx.resp_done = true;
                     }
@@ -399,7 +408,6 @@ pub unsafe extern "C" fn rs_dcerpc_udp_register_parser() {
     }
 }
 
-
 #[cfg(test)]
 mod tests {
     use crate::applayer::AppLayerResult;
index 6c2a2f9345df1d9c40b45937b2101e8288073bcd..1e62241bb215604efcb73bd5badf290711740cdb 100644 (file)
@@ -18,7 +18,7 @@
 // written by Victor Julien
 
 use uuid;
-use crate::smb::smb::*;
+use crate::smb::smb::{cfg_max_stub_size, *};
 use crate::smb::smb2::*;
 use crate::smb::dcerpc_records::*;
 use crate::smb::events::*;
@@ -205,10 +205,15 @@ pub fn smb_write_dcerpc_record(state: &mut SMBState,
                                 SCLogDebug!("previous CMD {} found at tx {} => {:?}",
                                         dcer.packet_type, tx.id, tx);
                                 if let Some(SMBTransactionTypeData::DCERPC(ref mut tdn)) = tx.type_data {
-                                    SCLogDebug!("additional frag of size {}", recr.data.len());
-                                    tdn.stub_data_ts.extend_from_slice(recr.data);
                                     tdn.frag_cnt_ts += 1;
-                                    SCLogDebug!("stub_data now {}", tdn.stub_data_ts.len());
+                                    let max_size = cfg_max_stub_size() as usize;
+                                    if recr.data.len() + tdn.stub_data_ts.len() < max_size {
+                                        SCLogDebug!("additional frag of size {}", recr.data.len());
+                                        tdn.stub_data_ts.extend_from_slice(recr.data);
+                                        SCLogDebug!("stub_data now {}", tdn.stub_data_ts.len());
+                                    } else if tdn.stub_data_ts.len() < max_size {
+                                        tdn.stub_data_ts.extend_from_slice(&recr.data[..max_size - tdn.stub_data_ts.len()]);
+                                    }
                                 }
                                 if dcer.last_frag {
                                     SCLogDebug!("last frag set, so request side of DCERPC closed");
@@ -240,12 +245,17 @@ pub fn smb_write_dcerpc_record(state: &mut SMBState,
                             SCLogDebug!("DCERPC: REQUEST {:?}", recr);
                             if let Some(SMBTransactionTypeData::DCERPC(ref mut tdn)) = tx.type_data {
                                 SCLogDebug!("first frag size {}", recr.data.len());
-                                tdn.stub_data_ts.extend_from_slice(recr.data);
                                 tdn.opnum = recr.opnum;
                                 tdn.context_id = recr.context_id;
                                 tdn.frag_cnt_ts += 1;
-                                SCLogDebug!("DCERPC: REQUEST opnum {} stub data len {}",
-                                        tdn.opnum, tdn.stub_data_ts.len());
+                                let max_size = cfg_max_stub_size() as usize;
+                                if tdn.stub_data_ts.len() + recr.data.len() < max_size {
+                                    tdn.stub_data_ts.extend_from_slice(recr.data);
+                                    SCLogDebug!("DCERPC: REQUEST opnum {} stub data len {}",
+                                            tdn.opnum, tdn.stub_data_ts.len());
+                                } else if tdn.stub_data_ts.len() < max_size {
+                                    tdn.stub_data_ts.extend_from_slice(&recr.data[..max_size - tdn.stub_data_ts.len()]);
+                                }
                             }
                             if dcer.last_frag {
                                 tx.request_done = true;
@@ -407,8 +417,13 @@ fn dcerpc_response_handle(tx: &mut SMBTransaction,
                     if let Some(SMBTransactionTypeData::DCERPC(ref mut tdn)) = tx.type_data {
                         SCLogDebug!("CMD 11 found at tx {}", tx.id);
                         tdn.set_result(DCERPC_TYPE_RESPONSE);
-                        tdn.stub_data_tc.extend_from_slice(respr.data);
+                        let max_size = cfg_max_stub_size() as usize;
                         tdn.frag_cnt_tc += 1;
+                        if tdn.stub_data_tc.len() + respr.data.len() < max_size {
+                            tdn.stub_data_tc.extend_from_slice(respr.data);
+                        } else if tdn.stub_data_tc.len() < max_size {
+                            tdn.stub_data_tc.extend_from_slice(&respr.data[..max_size - tdn.stub_data_tc.len()]);
+                        }
                     }
                     tx.vercmd.set_ntstatus(ntstatus);
                     tx.response_done = dcer.last_frag;
index 97d4bf4f22722b0f9fa701533d3360acba924aa2..4a2a346dc5a04627e2e8ff3bbcd7236efaaeb9b8 100644 (file)
@@ -81,6 +81,8 @@ pub static mut SMB_CFG_MAX_WRITE_SIZE: u32 = 16777216;
 pub static mut SMB_CFG_MAX_WRITE_QUEUE_SIZE: u32 = 67108864;
 pub static mut SMB_CFG_MAX_WRITE_QUEUE_CNT: u32 = 64;
 
+pub static mut SMB_DCERPC_MAX_STUB_SIZE: u32 = 1048576;
+
 static mut ALPROTO_SMB: AppProto = ALPROTO_UNKNOWN;
 
 static mut SMB_MAX_TX: usize = 1024;
@@ -2436,6 +2438,21 @@ pub unsafe extern "C" fn rs_smb_register_parser() {
                 SCLogError!("Invalid value for smb.max-tx");
             }
         }
+       let retval = conf_get("app-layer.protocols.smb.dcerpc.max-stub-size");
+       if let Some(val) = retval {
+           match get_memval(val) {
+               Ok(retval) => {
+                    if retval > 0 {
+                        SMB_DCERPC_MAX_STUB_SIZE = retval as u32;
+                    } else {
+                        SCLogError!("Invalid max-stub-size value");
+                    }
+               }
+               Err(_) => {
+                    SCLogError!("Invalid max-stub-size value");
+               }
+           }
+       }
         SCLogConfig!("read: max record size: {}, max queued chunks {}, max queued size {}",
                 SMB_CFG_MAX_READ_SIZE, SMB_CFG_MAX_READ_QUEUE_CNT, SMB_CFG_MAX_READ_QUEUE_SIZE);
         SCLogConfig!("write: max record size: {}, max queued chunks {}, max queued size {}",
@@ -2444,3 +2461,9 @@ pub unsafe extern "C" fn rs_smb_register_parser() {
         SCLogDebug!("Protocol detector and parser disabled for SMB.");
     }
 }
+
+#[inline(always)]
+pub fn cfg_max_stub_size() -> u32 {
+    unsafe { SMB_DCERPC_MAX_STUB_SIZE }
+}
+
index b49e261f2f310b1ac8f2c7194fab704328c747fe..b95d5e62f205eede1d28c36e4f542fac9055d414 100644 (file)
@@ -933,6 +933,8 @@ app-layer:
       enabled: yes
       # Maximum number of live DCERPC transactions per flow
       # max-tx: 1024
+      #max-stub-size: 1MiB
+
     ftp:
       enabled: yes
       # memcap: 64mb
@@ -997,6 +999,8 @@ app-layer:
 
       # Stream reassembly size for SMB streams. By default track it completely.
       #stream-depth: 0
+      #dcerpc:
+      #  max-stub-size: 1MiB
 
     nfs:
       enabled: yes